#include "Shader.h"
#include "Files.h"
#include <fstream>

Shader::Shader(std::string fname, GLuint type) : fileName(fname){
	FileBuffer file("shaders/"+fname);
	if (!file.Exists()){
		Engine::ThrowError("Cannot find shader: shaders/"+fname);
	}

	shader = glCreateShader(type);
}

void Shader::Compile(){
	
	FileBuffer file("shaders/" + fileName);
	string fileSource = file.Read().c_str();

	const char* ssrc = fileSource.c_str();
	glShaderSource(shader, 1, &ssrc, NULL);
	glCompileShader(shader);

	GLint status;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
	if (status != GL_TRUE){
		glGetShaderInfoLog(shader, sizeof(error), NULL, error);
		FileBuffer errorLog("Logs/shaders.log");
		if (!errorLog.Replace(error)){
			Engine::ThrowError("Cannot log shader error log");
		}
		Engine::ThrowError("Cannot compile shader. Error logged to Logs/shaders.log");
	}
}

GLuint Shader::GetShader(){
	return shader;
}

ShaderProgram::ShaderProgram(){
	program = glCreateProgram();
}

ShaderProgram::ShaderProgram(std::string vert, std::string frag){
	program = glCreateProgram();

	Shader vertex(vert, GL_VERTEX_SHADER);
	Shader fragment(frag, GL_FRAGMENT_SHADER);
	vertex.Compile();
	fragment.Compile();

	AttachToProgram(vertex);
	AttachToProgram(fragment);
	Build();
	UseShader();

	glUniform1i(glGetUniformLocation(program, "diffuse"), 0);
	glUniform1i(glGetUniformLocation(program, "normal"), 1);
	glUniform1i(glGetUniformLocation(program, "specular"), 2);
	glUniform1i(glGetUniformLocation(program, "illum"), 3);
	glUniform1i(glGetUniformLocation(program, "height"), 4);
}

ShaderProgram::ShaderProgram(std::string vert, std::string frag, std::string geom){
	program = glCreateProgram();

	Shader vertex(vert, GL_VERTEX_SHADER);
	Shader fragment(frag, GL_FRAGMENT_SHADER);
	Shader geometry(geom, GL_GEOMETRY_SHADER);
	vertex.Compile();
	fragment.Compile();
	geometry.Compile();

	AttachToProgram(vertex);
	AttachToProgram(fragment);
	AttachToProgram(geometry);
	Build();
	UseShader();

	glUniform1i(glGetUniformLocation(program, "diffuse"), 0);
	glUniform1i(glGetUniformLocation(program, "normal"), 1);
	glUniform1i(glGetUniformLocation(program, "specular"), 2);
	glUniform1i(glGetUniformLocation(program, "illum"), 3);
	glUniform1i(glGetUniformLocation(program, "height"), 4);
}

GLuint ShaderProgram::GetProgram(){
	return program;
}

void ShaderProgram::AttachToProgram(Shader shader){
	glAttachShader(program, shader.GetShader());
}

void ShaderProgram::Build(){
	glBindFragDataLocation(program, 0, "outColour");
	glLinkProgram(program);
}
void ShaderProgram::UseShader(){
	glUseProgram(program);
}